/*************************************************************************
    > File Name: main.c
    > Author: NKJ
    > Mail: tkyl01@yeah.net
    > Created Time: 2016年10月24日 星期一 13时56分00秒
 ************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include "uart.h"


#define SERIALPORTDEFULT                \
{                                       \
    .device = "/dev/ttyUSB0",           \
    .bps = 115200,                      \
    .dataBits = 8,                      \
    .parity = 0,                        \
    .stopBits = 1,                      \
    .rtsCts = 0,                        \
    .xOnXOff = 0,                       \
    .timeout = 100,                     \
    .enter = 0,                         \
    .hex = 0,                           \
    .echo = 0,                          \
    .cycle_send_bs = 'n',               \
    .cycle_send_data = "none",          \
    .cycle_send_time = 100,             \
    .sta = 0,                           \
}

const char *parity[]={"none","odd","even"};
const char *enter[]={"no press enter send","press enter send,tail + \"enter\"","press enter send, tail no enter"};
const char *hex[]={"ascii","hex recv","hex send","hex recv send"};
char cycle_data[256] = "none";
FILE *f_send = NULL;

struct _arg
{
    int c;
    char **v;
}arg;

struct _serialPort
{
    char device[256]; 
    uint32_t bps; 
    uint32_t dataBits; 
    uint32_t parity;
    uint32_t stopBits;
    uint32_t rtsCts; 
    uint32_t xOnXOff;
    int32_t timeout;
    uint32_t enter;
    uint32_t hex;
    uint32_t echo;
    char cycle_send_bs;
    char cycle_send_data[256];
    int32_t cycle_send_time;
    uint8_t sta;//opened(1) or closed(0)
};
struct _serialPort serialPort = SERIALPORTDEFULT;

pthread_t ntid1,ntid2,ntid3,ntid4;
uint8_t quit = 0;

uint32_t mint(char *data);
int mintl(char *data,int start,int end,int b);
int ma2bHex(char *str,uint8_t *data,int len);

void getUartSet(int c, char *v[]);
void *uartRxThread(void *p);
void *uartTxThread(void *p);

void *keyboardThread(void *p);
void *serialPortMonitorThread(void *p);

void print_usage(void)
{
    printf("usage:\r\n");
    printf("      miniuart <serial port:required> <baud rate:default 115200> [option]\r\n\r\n");
    printf("  baud support: 300,1200,2400,4800,9600,19200,38400,57600,115200,\r\n");
    printf("                230400,460800,500000,576000,921600,1000000,1152000,\r\n");
    printf("                1500000,2000000,2500000,3000000,3500000,4000000\r\n");
    printf("  option:\r\n");
    printf("      Dx : data bits                            [5,6,7,8;default is D8]\r\n");
    printf("      Px : parity bit                           [0 none,1 odd,2 even;default is P0]\r\n");
    printf("      Sx : stop bits                            [1,2;default is S1]\r\n");
    printf("      RCx: rts cts hardware handshaking used    [0,1;default is RC0]\r\n");
    printf("      Xx : x on off software handshaking used   [0,1;default is X0]\r\n");
    printf("      Tx : recv timeout                         [<0 block,>=0 ms until recv or time out;default is T100]\r\n");
    printf("      Ex : press enter send && tail \"enter\"     [0(n),1(s+),2(s);default is E0]\r\n");
    printf("      Hx : data style is HEX                    [0(n),1(r),2(s),3(rs);default is H0]\r\n");
    printf("      Ox : echo on off                          [0,1;default is O0]\r\n");
    printf("      Cs : cycle send                           [b(bin)/s(str)/p(fpath)\"str...\"cycletime(ms);Cb\",AA FA 33 A1,\"100]\r\n");
}


int main(int argc, char *argv[])
{
    int err;

    arg.c = argc - 1;
    arg.v = argv + 1;

    if(arg.c)
    {
        for(int i = 0;i < arg.c;i ++)
        {
            if((!strcmp(arg.v[i],"-h")) || (!strcmp(arg.v[i],"-H")))
            {
                print_usage();
                exit(0);
            }
            else
            if((!strcmp(arg.v[i],"-v")) || (!strcmp(arg.v[i],"-V")))
            {
                printf("miniuart v1.0\r\n");
                exit(0);
            }
        }
    }
    else
    {
        printf("Unknown command. '-h' for help.\r\n");
        print_usage();
        exit(0);
    }

    printf("\r\nHellow miniuart !\r\n\r\n");

    getUartSet(arg.c,arg.v);
    if(!uartOpen((int8_t*)serialPort.device,serialPort.bps,serialPort.dataBits,serialPort.parity,
            serialPort.stopBits,serialPort.rtsCts,serialPort.xOnXOff,serialPort.timeout))//success
    {
        err = pthread_create(&ntid1, NULL, uartRxThread, NULL);
        if(err != 0) 
        {
            printf("can't create thread uartRxThread: %s\n", strerror(err));
            exit(1);
        }
        err = pthread_create(&ntid2, NULL, uartTxThread, NULL);
        if(err != 0) 
        {
            printf("can't create thread uartTxThread: %s\n", strerror(err));
            exit(1);
        }
        err = pthread_create(&ntid3, NULL, keyboardThread, NULL);
        if(err != 0) 
        {
            printf("can't create thread keyboardThread: %s\n", strerror(err));
            exit(1);
        }
        err = pthread_create(&ntid4, NULL, serialPortMonitorThread, NULL);
        if(err != 0) 
        {
            printf("can't create thread serialPortMonitorThread: %s\n", strerror(err));
            exit(1);
        }
        serialPort.sta = 1;
    }
    
    pthread_join(ntid1,NULL);
    pthread_join(ntid2,NULL);
    pthread_join(ntid3,NULL);
    pthread_join(ntid4,NULL);
    
    return 0;
}

uint32_t mint(char *data)
{
    uint32_t i = 0,j = 0;
    if(data[1] == 'X' || data[1] == 'x') 
    {
        data += 2;
        while(*data)
        {
            if(*data <= '9')
                j = *data - '0';
            else if(*data <= 'F') 
                j = *data - 'A' + 10;
            else if(*data <= 'f') 
                j = *data - 'a' + 10; 
                          
            i = i * 16 + j;
            data ++;
        }
    }
    else
    {
        return atoi(data);
    }
    return i;
}

int mintl(char *data,int start,int end,int b)
{
    char buf[13] = {'0','X',0,0,0,0,0,0,0,0,0,0,0};
    int i = 0;
    if((end - start) > 10)
        return 0;
    if(b == 10)
    {
        buf[0] = 0;
        buf[1] = 0;
    }
    for(i = 0;i < (end - start);i ++)
    {
        if(b == 16)
            buf[2 + i] = data[start + i];
        else if(b == 10)
            buf[i] = data[start + i];
        else
            break;
    }
    return mint(buf);
}

int ma2bHex(char *str,uint8_t *data,int len)
{
    int i = 0;
    if(str[len] != ' ')
        len ++;
    len = 3 * (len / 3);
    for(i = 0;i < len;i += 3)
    {
        data[i/3] = mintl(str,i,i + 2,16);
    }
    //data[i/3] = 0;
    return len;
}

char getch(void)
{
    char c;
    system("stty -echo");
    system("stty -icanon");
    c = getchar();
    system("stty icanon");
    system("stty echo");
    return c;
}

void getUartSet(int c, char *v[])
{
    if(c)
    {
        strcpy(serialPort.device ,v[0]);
        c -= 1;
        v += 1;
    }
    if(c)
    {
        if(v[0][0] >= '0' && v[0][0] <= '9')
        {
            serialPort.bps = mint(v[0]);
            c -= 1;
            v += 1;
        }
    }
    while(c)
    {
        switch(v[0][0])
        {
            case 'D':
                if(strlen(v[0]) > 1)
                    if((v[0][1] >= '0') && (v[0][1] <= '9'))
                        serialPort.dataBits = v[0][1] - '0';
            break;
            case 'P':
                if(strlen(v[0]) > 1)
                    if((v[0][1] >= '0') && (v[0][1] <= '9'))
                        serialPort.parity = v[0][1] - '0';
            break;
            case 'S':
                if(strlen(v[0]) > 1)
                    if((v[0][1] >= '0') && (v[0][1] <= '9'))
                        serialPort.stopBits = v[0][1] - '0';
            break;
            case 'R':
                if(strlen(v[0]) > 2)
                    if((v[0][2] >= '0') && (v[0][2] <= '9'))
                        serialPort.rtsCts = v[0][2] - '0';
            break;
            case 'X':
                if(strlen(v[0]) > 1)
                    if((v[0][1] >= '0') && (v[0][1] <= '9'))
                        serialPort.xOnXOff = v[0][1] - '0';
            break;
            case 'T':
                if(strlen(v[0]) > 1)
                    serialPort.timeout = mint(&v[0][1]);
            break;
            case 'E':
                if(strlen(v[0]) > 1)
                    if((v[0][1] >= '0') && (v[0][1] <= '9'))
                        serialPort.enter = v[0][1] - '0';
            break;
            case 'H':
                if(strlen(v[0]) > 1)
                    if((v[0][1] >= '0') && (v[0][1] <= '9'))
                        serialPort.hex = v[0][1] - '0';
            break;
            case 'O':
                if(strlen(v[0]) > 1)
                    if((v[0][1] >= '0') && (v[0][1] <= '9'))
                        serialPort.echo = v[0][1] - '0';
            break;
            case 'C':
                sscanf(v[0],"%*[^,],%[^,],%d",cycle_data,&serialPort.cycle_send_time);                
                if(v[0][1] == 'b')
                {
                    serialPort.cycle_send_bs = 'b';
                    ma2bHex(cycle_data,(uint8_t *)serialPort.cycle_send_data,strlen(cycle_data));
                }
                else if(v[0][1] == 's')
                {
                    serialPort.cycle_send_bs = 's';
                    strcpy((char *)serialPort.cycle_send_data,cycle_data);
                }
                else if(v[0][1] == 'p')
                {
                    serialPort.cycle_send_bs = 'p';
                    strcpy((char *)serialPort.cycle_send_data,cycle_data);
                    if((f_send = fopen(serialPort.cycle_send_data,"rb")) == NULL)
                    {
                        printf("no find file: %s\r\n",serialPort.cycle_send_data);
                        exit(0);
                    }
                }
            default:
            break;
        }
        c -= 1;
        v += 1;
    }

    printf("port is     : %s\r\n",serialPort.device);
    printf("baud rate   : %d\r\n",serialPort.bps);
    printf("data bits   : %d\r\n",serialPort.dataBits);
    printf("parity is   : %s\r\n",parity[serialPort.parity%3]);
    printf("stop bits   : %d\r\n",serialPort.stopBits);
    printf("hard flow   : %s\r\n",serialPort.rtsCts ? "rtc cts" : "none");
    printf("soft flow   : %s\r\n",serialPort.xOnXOff? "x on off" : "none");
    printf("recv timeout: %d ms\r\n",serialPort.timeout);
    printf("enter send  : %s\r\n",enter[serialPort.enter%3]);
    printf("data style  : %s\r\n",hex[serialPort.hex%4]);
    printf("local echo  : %s\r\n",serialPort.echo ? "on" : "off");
    printf("cycle send  : %c,%s,%dms\r\n",serialPort.cycle_send_bs,cycle_data,serialPort.cycle_send_time);

    printf("\r\n");
}

void *uartRxThread(void *p)
{
    char c = 0;
    while(!quit)
    {
        if(serialPort.sta != 1)
            continue;

        if(uartRx(1,(uint8_t*)&c) == (1))
        {
            if(serialPort.hex == 1 || serialPort.hex == 3)
                printf("%02X ",c & 0xff);
            else
                printf("%c",c);
                
            fflush(stdout);
        } 
    }
    return NULL;
}

void *uartTxThread(void *p)
{
    while(!quit)
    {
        if(serialPort.cycle_send_time > 0)
            usleep(1000*serialPort.cycle_send_time);

        if(serialPort.sta != 1)
            continue;

        //定时发送 
        if(serialPort.cycle_send_bs == 'b' || serialPort.cycle_send_bs == 's')
        {
            uartTx(strlen(serialPort.cycle_send_data),(uint8_t*)serialPort.cycle_send_data);
            if(serialPort.enter == 1) uartTx(2,(uint8_t*)"\r\n");
        }
        else if(serialPort.cycle_send_bs == 'p')
        {
            if(feof(f_send))
                break;
            uint8_t l=fread(cycle_data,1,64,f_send);
            uartTx(l,(uint8_t*)cycle_data);
        }
    }
    fclose(f_send);
    return NULL;
}

void *keyboardThread(void *p)
{
    char scanfstr[256] = {0};
    uint32_t count = 0;
    uint8_t ctrl_a = 0;
    while(1)
    {
        *(scanfstr + count) = getch();
        if((ctrl_a == 0x01) && (*(scanfstr + count) == 'q'))
            break;
        if(*(scanfstr + count) == 0x01)
            ctrl_a = 0x01;
        else
            ctrl_a = 0;
        if(serialPort.echo)
        {
            printf("%c",*(scanfstr + count));
        }

        if(serialPort.sta != 1)
            continue;

        if((serialPort.enter) && ((*(scanfstr + count) == '\r') || (*(scanfstr + count) == '\n')))
        {
            if(serialPort.enter == 1)
            {
                if(serialPort.hex == 2 || serialPort.hex == 3)
                {
                    //16进制发送,解析成16进制
                    uint8_t data[count/3];
                    uint8_t l = ma2bHex(scanfstr,data,count);
                    uartTx(l/3,data);
                    uartTx(2,(uint8_t*)"\r\n");
                }
                else
                {
                    *(scanfstr + count) = '\r';
                    *(scanfstr + count + 1) = '\n';
                    uartTx(count + 2,(uint8_t*)scanfstr);
                }
            }
            else if(serialPort.enter == 2)
            {
                if(serialPort.hex == 2 || serialPort.hex == 3)
                {
                    //16进制发送,解析成16进制
                    uint8_t data[count/3];
                    uint8_t l = ma2bHex(scanfstr,data,count);
                    uartTx(l/3,data);
                }
                else
                {
                    *(scanfstr + count) = '\0';
                    uartTx(count + 1,(uint8_t*)scanfstr);
                }
            }
            count = 0;
        }
        else if(!serialPort.enter)
        {
            if(serialPort.hex == 2 || serialPort.hex == 3)
            {
                count ++;
                if(count == 3)
                {
                    //16进制发送,解析成16进制
                    uint8_t data;
                    ma2bHex(scanfstr,&data,count);
                    uartTx(1,&data);
                    count = 0;
                }
            }
            else
            {
                uartTx(1,(uint8_t*)scanfstr);
                count = 0;
            }
        }
        else
        {
            count ++; 
            count = count % 256;
        }
    }
    quit = 1;
    uartClose();
    exit(0);
    return NULL;
}

void *serialPortMonitorThread(void *p)
{
    time_t cur_t;  
    struct tm* cur_tm;  
    char buf[32] = {0};
  
    while(!quit)
    {
        if((access(serialPort.device,F_OK)) != (-1))   
        {   
            if(!serialPort.sta)
            {
                if(!uartOpen((int8_t*)serialPort.device,serialPort.bps,serialPort.dataBits,serialPort.parity,
                            serialPort.stopBits,serialPort.rtsCts,serialPort.xOnXOff,serialPort.timeout))
                {
                    serialPort.sta = 1;
                    time(&cur_t);  
                    cur_tm=localtime(&cur_t);  
                    memset(buf,0,32);
                    sprintf(buf,"%s",asctime(cur_tm));
                    buf[strlen(buf) - 1] = 0;
                    printf("[%s] serial port: %s opened\r\n",buf,serialPort.device);   
                }
            }
        }   
        else  
        {   
            if(serialPort.sta == 1)
            {
                uartClose();
                serialPort.sta = 0;
                time(&cur_t);  
                cur_tm=localtime(&cur_t);  
                memset(buf,0,32);
                sprintf(buf,"%s",asctime(cur_tm));
                buf[strlen(buf) - 1] = 0;
                printf("[%s] serial port: %s closed\r\n",buf,serialPort.device);   
            }
        }      
        sleep(1);
    }
    return NULL;
}

















